#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <time.h>
#include <string.h>
#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
#include <fcntl.h>
#include <signal.h>

#define HELLO_PORT 12345
#define HELLO_GROUP "239.42.5.1"

char *IP;
int size = 0;
typedef struct exist_files
{
    char *file;
    char *add1;
    char *add2;

    struct exist_files *next;
}t_exist_files;

char* get_md5(char *filename)
{

  FILE *fd;
  FILE *f;
  char *tmp;

  tmp = malloc (60 * sizeof(char));
  tmp = strcpy(tmp, "md5sum ");
  tmp = strcat (tmp, filename);
  tmp = strcat (tmp, " > md5.tmp");
  printf("test\n");
  f = fopen(filename, "r");
  if (f == NULL)
    return NULL;
  else
    fclose(f);
  system(tmp);

  fd = fopen("md5.tmp","r");

  if (fd != NULL)
    {
      fscanf (fd, "%s", tmp);
    }

  fclose(fd);
  return tmp;
}


void free_all(struct exist_files * e)
{
  struct exist_files *tmp = e;
  while (e->next != NULL)
  {
    tmp = e;
    e = e->next;
    free(tmp);
  }
}

int find_exist(char *file, struct exist_files *exist_file_l, char *tmp_ip)
{
  int bool = 0;
  int i = 0;

  struct exist_files *tmp = exist_file_l;

  for (i = 0; i < size - 1; i++)
  {
    if ((tmp->file != NULL) && (strcmp(tmp->file, file) == 0))
    {
      if (tmp_ip != NULL)
      {
	if (strcmp(tmp->add1, tmp_ip) && tmp->add2 == NULL)
	  tmp->add2 = tmp_ip;
      }
      bool = 1;
      break;
    }
    if (tmp->next != NULL)
      tmp = tmp->next;
    else
      break;
  }
  return bool;
}

void ifNew(char *file, struct exist_files *e, char *tmp_ip)
{
  struct exist_files *tmp;
  struct exist_files *new = malloc(sizeof (struct exist_files));

  size++;
  tmp = e;

  new->next = NULL;
  new->file = NULL;

  while(tmp->next != NULL)
    tmp = tmp->next;
  if (tmp->file == NULL)
    tmp->file = file;
  else
  {
    if (new->file == NULL)
    {
      new->file = file;
      new->add1 = tmp_ip;
    }
  }
  tmp->next = new;
}


struct exist_files *my_exist_file(struct exist_files *e,
				  struct exist_files *tmp)
{
  struct exist_files *t = e;

  if (e == NULL)
    return NULL;
  if (t->file != NULL && ((tmp->add1 != NULL && strcmp(t->add1,IP) == 0)|| (tmp->add2 != NULL && strcmp(t->add2,IP) ==0)))
    tmp->file = t->file;
  while(t->next != NULL)
  {
    if (t->file != NULL && (strcmp(t->add1,IP) == 0|| strcmp(t->add2,IP) ==0))
      tmp->file = t->file;
    tmp->next = t->next;
    t = t->next;
  }
  return (tmp);
}


void show_list(struct exist_files *e)
{
  struct exist_files *tmp = e;

  while(tmp->next != NULL)
  {
     printf("%s\n",tmp->file);
     tmp = tmp->next;
  }
}

void my_sync(char *msg)
{
  FILE *fd = 0;
  printf("test\n");
  if ((fd = fopen("exist_files", "w"))  != NULL)
  {
    fprintf(fd, "%s\n",msg);
    printf("recived files existance on :%s\n",msg);
    fclose(fd);
    return;
  }
  else
  {
    perror("cant write locally");
    exit(2);
  }
}

struct exist_files *update_files(struct exist_files *e)
{
  FILE *fd = 0;
  char *msg = malloc(sizeof (char) * 1024);

  if ((fd = fopen("exist_files", "r")) != NULL)
  {
    fscanf(fd, "%s", msg);
  }
  free(msg);
  return e;
}

void get_ip2()
{

  FILE *fd;

  system("ip -f inet -o addr | grep eth | tr -s ' ' | cut -d ' ' -f 4-8| cut -d '/' -f 1 > ip.txt");

  fd = fopen("ip.txt","r");

  if (fd != NULL)
    {
      fscanf (fd, "%s", IP);
    }

  fclose(fd);
  system("rm ip.txt");
}

struct exist_files *my_send(char *msg, struct exist_files *e)
{
  struct sockaddr_in addr;
  int fd;
  char *message = NULL;
  char *addrIP = NULL;
  int port = 0;
  char *str = NULL;
  char *t = NULL;
  char *md5;
  char *mfile;
  char *mcal;

  struct exist_files *tmp = malloc( sizeof (struct exist_files));

  tmp->next = NULL;
  tmp->file = NULL;

  str = calloc(1, sizeof (char) * 1024);
  if (strncmp(msg,"1|", 2) == 0)
  {
    printf("received request of @ IP\n");
    addrIP = strtok(msg,"|");
    addrIP = strtok(NULL,"|");
    strcpy(str,"4|");
    port = 54321;
  }

  if (strncmp(msg, "2|", 2) == 0)
  {
    printf("received request of synchronisation\n");
    addrIP = strtok(msg,"|");
    addrIP = strtok(NULL,"|");

    if (strcmp(addrIP,IP) != 0)
    {
      addrIP = HELLO_GROUP;
      port = 12345;
      strcpy(str,"3|");
    }
    else
    {
      free(tmp);
      return e;
    }
  }

  if (strncmp(msg,"3|",2) == 0)
  {
    t = malloc (strlen(msg));
    strcpy(t, msg);
    addrIP = strtok(t,"|");
    addrIP = strtok(NULL,"|");
    if (strcmp(addrIP,IP) != 0)
    {
      printf("recived files existence on:%s\n",msg);
      my_sync(msg);
    }
    free(t);
    free(tmp);
    return e;
  }

  if (strncmp(msg, "5|",2) ==0)
  {
    char *t = msg;
    printf("%s\n",msg);
    addrIP = HELLO_GROUP;
    port = 12345;
    strtok(NULL, "|");
    mfile = strtok(NULL, "|");
    mcal = get_md5(mfile);
    printf("check md5 of %s\n", mfile);
    if (mcal != NULL)
    {
      printf ("test\n");
      md5= strtok(NULL, "|");
      if (strcmp(md5,mcal) == 0)
	str = strcpy(str,"7|");
      else
	str = strcpy(str, "8|");
      str = strcat(str,mfile);
      str = strcat(str, "|");
    }
    else
    {
      printf("check md5 but don not have the file\n");
      return e;
    }
  }
  if (strncmp(msg, "8|", 2) == 0)
  {
    strtok(msg, "|");
    mfile = strtok(NULL,"|");
    addrIP = strtok(NULL, "|");
    printf("the file %s we have and the same name file on %s is not same version",addrIP, mfile);
    return e;
  }
  if (strncmp(msg, "7|", 2) == 0)
  {
    strtok(msg, "|");
    mfile = strtok(NULL,"|");
    addrIP = strtok(NULL, "|");
    printf("the file %s we have and the same name file on %s is same version",addrIP, mfile);
    return e;
  }
  strcat(str, IP);
  strcat(str,"|");
  if (strncmp(msg,"2|",2) == 0)
  {
    tmp = my_exist_file(e, tmp);
    if (tmp != NULL && tmp->file != NULL)
    {
      strcat(str,tmp->file);
      strcat(str, "|");
    }
    while (tmp != NULL && tmp->next != NULL)
    {
      tmp = tmp->next;
      strcat(str, tmp->file);
      strcat(str, "|");
    }
    free(tmp);
  }
  message = str;
  printf("message send:%s",message);
  /* create what looks like an ordinary UDP socket */
  if ((fd=socket(AF_INET,SOCK_DGRAM,0)) < 0)
  {
    perror("socket");
    free_all(e);
    free(tmp);
    exit(1);
  }

  /* set up destination address */
  memset(&addr,0,sizeof(addr));
  addr.sin_family=AF_INET;
  addr.sin_addr.s_addr=inet_addr(addrIP);
  addr.sin_port=htons(port);

  /* now just sendto() our destination! */
  if (sendto(fd,message, strlen(message),0,(struct sockaddr *) &addr,
	     sizeof(addr)) < 0)
  {
    perror("sendto");
    free_all(e);
    free(tmp);
    exit(1);
  }
  free(tmp);
  return (e);
}

void sig_handler(int signo)
{
  if (signo == SIGINT)
  {
    printf("received SIGINT\n");
    system("killall a.out");
  }

}

void listen_multicast(struct exist_files * e)
{
  struct sockaddr_in addr;
  int fd, nbytes,addrlen;
  struct ip_mreq mreq;
  char msgbuf[256] = {0};
  FILE *log = 0;
  u_int yes = 1;            /*** MODIFICATION TO ORIGINAL */
  pid_t pid = 0;

  /* create what looks like an ordinary UDP socket */
  if ((fd=socket(AF_INET,SOCK_DGRAM,0)) < 0)
  {
    perror("socket");
    free_all(e);
    exit(1);
  }


/**** MODIFICATION TO ORIGINAL */
  /* allow multiple sockets to use the same PORT number */
  if (setsockopt(fd,SOL_SOCKET,SO_REUSEADDR,&yes,sizeof(yes)) < 0)
  {
    perror("Reusing ADDR failed");
    exit(1);
  }
/*** END OF MODIFICATION TO ORIGINAL */

  /* set up destination address */
  memset(&addr,0,sizeof(addr));
  addr.sin_family=AF_INET;
  addr.sin_addr.s_addr=htonl(INADDR_ANY); /* N.B.: differs from sender */
  addr.sin_port=htons(HELLO_PORT);

  /* bind to receive address */
  if (bind(fd,(struct sockaddr *) &addr,sizeof(addr)) < 0)
  {
    perror("bind");
    free_all(e);
    exit(1);
  }

  /* use setsockopt() to request that the kernel join a multicast group */
  mreq.imr_multiaddr.s_addr=inet_addr(HELLO_GROUP);
  mreq.imr_interface.s_addr=htonl(INADDR_ANY);
  if (setsockopt(fd,IPPROTO_IP,IP_ADD_MEMBERSHIP,&mreq,sizeof(mreq)) < 0)
  {
    perror("setsockopt");
    free_all(e);
    exit(1);
  }

  /* now just enter a read-print loop */
  while (1)
  {
    addrlen=sizeof(addr);
    if ((nbytes=recvfrom(fd,msgbuf,1024,0,
			 (struct sockaddr *) &addr,(socklen_t *) &addrlen)) < 0)
    {
      perror("recvfrom");
      free_all(e);
      exit(1);
    }
    if (nbytes > 0)
    {
      pid = fork();
      if (pid != 0)
      {
	my_send(msgbuf, e);
	memset(&msgbuf, 0, sizeof(msgbuf));
      }
      else
      {
	if (signal(SIGINT, sig_handler) == SIG_ERR)
	{
	  fclose(log);
	  free_all(e);
	  exit(1);
	}
	e = update_files(e);
      }
    }
  }
}

int main(void)
{
  struct exist_files	*e = malloc(sizeof (struct exist_files));
  FILE *log = fopen("log", "w+");
  if (log != NULL)
  {
    fprintf(log,"%s","start SAN server\n");
    fclose(log);
  }
  else
  {
    perror("log");
    exit(3);
  }
  e->next = NULL;
  e->file = "toto";
  e->add1 = IP;
  e->add2 = NULL;

  IP = calloc(1,sizeof (char) * 16);
  get_ip2();
  listen_multicast(e);
  free_all(e);
  return 0;
}

